#pragma once

#include "sock.hpp"
#include "epoller.hpp"
#include "util.hpp"
#include "protocol.hpp"
#include <unordered_map>
#include <functional>

using namespace std;

const int defaultfd = -1;
const int defaultnum = 64;

class tcpServer;
struct Connection;

using func = function<void(Connection*)>;

/*
为什么要创建connection
1.读事件到来时 非阻塞循环读取没有读到完整的报文 等待读事件再次就绪 下次读取的数据需要和上次读取的数据结合 所以需要空间存放每个sock的输入缓冲区
2.每个sock都能绑定相应的方法 在事件到来时做不同的处理 创建connection可以实现每个sock的个性化处理
*/
struct Connection
{
    Connection(int sock, tcpServer* tsp)
    : _sock(sock),
      _tsp(tsp)
    {}

    void Register(func recver, func sender, func excepter)
    {
        _recver = recver;
        _sender = sender;
        _excepter = excepter;
    }

    int _sock;
    string _inbuffer;//输入缓冲区
    string _outbuffer;//输出缓冲区
    func _recver;
    func _sender;
    func _excepter;
    tcpServer* _tsp;
};


/*
假设
1.request和response形成的报文大小很大 可能需要recv或send多次
2.一次接收客户端多个报文 需要解决粘包问题
*/
class tcpServer
{
public:
    tcpServer(uint16_t port, func f)
    : _port(port),
      _f(f),
      _revs(nullptr)
    {}


    ~tcpServer()
    {
        if (_revs)
            delete[] _revs;
    }

    void initServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        _ep.Create();
        //由于Accepter Recver Sender等类内成员函数有this指针 与func类型不匹配 需要进行bind处理
        //将listensock添加到epoll模型和Connection hash节点中 listensock不关心写和异常
        addConnection(_listensock.getFd(), EPOLLIN | EPOLLET, bind(&tcpServer::Accepter, this, placeholders::_1), nullptr, nullptr);
        _revs = new struct epoll_event[defaultnum];//创建就绪队列
        _num = defaultnum;
    }

    void startServer()
    {
        int timeout = -1;//设置epoll每轮关心的时间
        while (true)
        {
            int n = _ep.Wait(_revs, _num, timeout);
            switch (n)
            {
            case 0:
                cout << "time out" << endl;
                break;
            case -1:
                cout << "epoll wait error, " << strerror(errno) << endl;
                break;
            default:
                cout << "have event ready" << endl;
                handlerEvents();
                break;
            }
        }
    }

    void enableReadWrite(int sock, bool readable, bool writeable)
    {
        uint32_t events = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
        _ep.Control(sock, events, EPOLL_CTL_MOD);
    }

private:
    void addConnection(int sock, uint32_t events, func recver, func sender, func excepter)
    {
        if (events & EPOLLET)//ET模式 文件描述符设为非阻塞
            Util::setNonBlock(sock);
        Connection* conn = new Connection(sock, this);//为sock创建Connection对象
        conn->Register(recver, sender, excepter);//注册事件回调
        _ep.Control(sock, events, EPOLL_CTL_ADD);//将sock添加到epoll模型 设置为ET模式并关心读事件
        _connections.insert(make_pair(sock, conn));//维护sock对应的Connection
        cout << "add new sock: " << sock << " in epoll and hash" << endl;
    }

    void Accepter(Connection* conn)//listensock读事件处理方法
    {
        struct sockaddr_in client;
        int err;
        /*
        listensock一次读事件就绪时可能有多个连接到来 ET模式下如果不一次性将连接全接收
        则下次事件派发如果没有新连接到来不会再收到通知 之前留下的连接不能被接收 所以需要死循环式接收全部连接
        */
        while (true)
        {
            memset(&client, 0, sizeof client);   
            int sock = _listensock.Accept(&client, &err);
            if (sock > 0)
            {
                string clientip = inet_ntoa(client.sin_addr);
                uint16_t clientport = ntohs(client.sin_port);
                //读事件为常设事件 写事件按需设置
                addConnection(sock, EPOLLIN | EPOLLET, bind(&tcpServer::Recver, this, placeholders::_1), 
                                                       bind(&tcpServer::Sender, this, placeholders::_1),
                                                       bind(&tcpServer::Excepter, this, placeholders::_1));
                cout << "accept a new link, " << clientip << "[" << clientport << "]" << endl;
            }
            else
            {
                if (err == EAGAIN || err == EWOULDBLOCK)//本轮连接接收完毕 读事件未就绪
                    break;
                else if (err == EINTR)//由于信号导致读取中断
                    continue;
                else//读取错误
                {
                    cout << "accept error, " << strerror(errno) << endl;
                    exit(ACCEPT_ERR);
                }
            }
        }
    }

    void Recver(Connection* conn)
    {
        int sock = conn->_sock;
        //ET模式下 需要一次性将客户端发来的消息全部读取完成
        while (true)
        {
            char buffer[1024];
            int n = recv(sock, buffer, sizeof buffer - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                conn->_inbuffer += buffer;
            }
            else if (n == 0)
            {
                cout << "cient quit" << endl;
                if (conn->_excepter)
                    conn->_excepter(conn);
                return;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    cout << "recv error, " << strerror(errno) << endl;
                    if (conn->_excepter)
                        conn->_excepter(conn);
                    return;
                }
            }
        }
        _f(conn);
    }

    void Sender(Connection* conn)
    {
        int sock = conn->_sock;
        //ET模式下 需要循环发送数据 直到数据发送完成或写条件不具备时停止 
        while (true)
        {
            //服务器不需要解决粘包问题 多个报文可以混到一起发送
            int n = send(sock, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
            if (n > 0)
            {
                conn->_outbuffer.erase(0, n);
                if (conn->_outbuffer.empty())
                    break;  
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)//发送窗口已满 不具备写条件
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    cout << "send error, " << strerror(errno) << endl;
                    if (conn->_excepter)
                        conn->_excepter(conn);
                    return;
                }
            }
        }
        if (conn->_outbuffer.empty())
            conn->_tsp->enableReadWrite(conn->_sock, true, false);
        else//写条件不具备导致响应未发送完成 设置写事件关心 等待写条件具备时调用sender
            conn->_tsp->enableReadWrite(conn->_sock, true, true);
    }

    void Excepter(Connection* conn)
    {
        _ep.Control(conn->_sock, 0, EPOLL_CTL_DEL);
        _connections.erase(conn->_sock);
        close(conn->_sock);
        delete conn;
    }

    bool isConnectionExist(int sock)
    {
        return _connections.find(sock) != _connections.end();
    }

    void handlerEvents()
    {
        for (int i = 0; i < _num; i++)
        {
            int sock = _revs[i].data.fd;
            uint32_t events = _revs[i].events;
            //异常时间作为读写事件处理
            if (events & EPOLLERR)
                events |= EPOLLIN | EPOLLOUT;
            if (events & EPOLLHUP)
                events |= EPOLLIN | EPOLLOUT;
            //读事件到来 sock对应的connection对象存在 connection对象注册了读事件处理方法 
            if ((events & EPOLLIN) && isConnectionExist(sock) && _connections[sock]->_recver)
                _connections[sock]->_recver(_connections[sock]);
            if ((events & EPOLLOUT) && isConnectionExist(sock) && _connections[sock]->_sender)
                _connections[sock]->_sender(_connections[sock]);
        }
    }

private:
    uint16_t _port;
    Sock _listensock;
    Epoller _ep;
    unordered_map<int, Connection*> _connections;
    struct epoll_event* _revs;
    int _num;//_revs的大小 用于后续遍历
    func _f;//主文件设计的客户端消息处理逻辑 即应用层服务
};